#include "include/htbengine.h"
#include <iostream.h>
#include "include/constantvalues.h"
#include "model.h"

HtbEngine::HtbEngine(Value *settings)
{
	this->settings=settings;
	filterParentID="1:";
	qdiscParentID="1:1";
	defaultID=10;
	ruleCount=1;

	//connect(execTcStats,SIGNAL(readyRead()),this,SLOT(retrieveRuleStats()));

}
int HtbEngine::init()
{
	Value lsettings=*settings;
	QString dev=QString().fromStdString(lsettings["device"].asString());
	QString tc=QString().fromStdString(lsettings["tc_cmd"].asString());
	QString ifb=QString().fromStdString(lsettings["ifb"].asString());
	QString uplink=QString().fromStdString(lsettings["uplink"].asString());
	QString downlink=QString().fromStdString(lsettings["downlink"].asString());
	QString defaultBucketRate=QString().fromStdString(lsettings["default_bucket_rate"].asString());
	QString burst=QString().fromStdString(lsettings["default_burst"].asString());
	QString defaultPrio=QString().fromStdString(lsettings["default_priority"].asString());

	
	clear();

	debug();

	//Ingress
	//Redirect everything to the ifb device.
	tcCmd("qdisc add dev "+dev+" ingress");
	tcCmd("filter add dev "+dev+" parent ffff: protocol ip prio 1 u32 match u32 0 0 flowid 1: action mirred egress redirect dev "+ifb);

	//Attach initial qdiscs to ifb
	tcCmd("qdisc add dev "+ifb+" root handle 1: htb default "+QString().setNum(defaultID));
	tcCmd("class add dev "+ifb+" parent 1: classid 1:1 htb rate "+downlink+"kbit ceil "+downlink+"kbit");
	tcCmd("class add dev "+ifb+" parent 1:1 classid 1:"+QString().setNum(defaultID)+" htb rate "+defaultBucketRate+"kbit ceil "+downlink+"kbit prio "+defaultPrio);
        tcCmd("qdisc add dev "+ifb+" parent 1:"+QString().setNum(defaultID)+" handle 10 sfq perturb 10");

	//Egress
	tcCmd("qdisc add dev "+dev+" root handle 1: htb default "+QString().setNum(defaultID));
	tcCmd("class add dev "+dev+" parent 1: classid 1:1 htb rate "+uplink+"kbit ceil "+uplink+"kbit");
	tcCmd("class add dev "+dev+" parent 1:1 classid 1:"+QString().setNum(defaultID)+" htb rate "+defaultBucketRate+"kbit ceil "+uplink+"kbit prio "+defaultPrio);
        tcCmd("qdisc add dev "+dev+" parent 1:"+QString().setNum(defaultID)+" handle 10 sfq perturb 10");
}
int HtbEngine::addRule(Value rule)
{
	if(ruleCount==1)
		init();

	ruleCount++;
	if(ruleCount==defaultID)
		ruleCount++;

	//qDebug() << QString().setNum(ruleCount);

	Value lsettings=*settings;
	QString flowID="1:"+QString().setNum(ruleCount);
	
	QString interface;
	QString uplink=QString().fromStdString(lsettings["uplink"].asString());
	QString downlink=QString().fromStdString(lsettings["downlink"].asString());
	QString ceil;


	if(strcmp(rule["direction"].asCString(),"incoming")==0)
	{
		interface=QString().fromStdString(settings->get("ifb","").asString());	
		ceil=downlink;
	}
	else
	{
		interface=QString().fromStdString(settings->get("device","").asString());	
		ceil=uplink;
	}


	//Add classes
	tcCmd("class add dev "+interface+" parent 1:1 classid "+flowID+" htb rate "+QString().fromStdString(rule["rate"].asString())+"kbit ceil "+QString().fromStdString(rule["limit"].asString())+"kbit prio "+QString().fromStdString(rule["priority"].asString()));

	//Add qdiscs
	tcCmd("qdisc add dev "+interface+" parent "+flowID+" handle "+QString().setNum(ruleCount)+" sfq perturb 10");

	
	//Add filters
	for(int filterCount=1; filterCount<rule["filters"].size(); filterCount++)
	{
		for(int filterEntryCount=1; filterEntryCount<rule["filters"][filterCount].size(); filterEntryCount++)
		{
			if(rule["filters"][filterCount][filterEntryCount].empty())
				continue;
			if(strcmp(rule["filters"][filterCount][filterEntryCount]["path"].asCString(),".u32")==0)
			{
				//Add device to filter
				rule["filters"][filterCount][filterEntryCount]["value"][1]=interface.toStdString();
				//Add png().setNum(defaultID)arent to filter
				rule["filters"][filterCount][filterEntryCount]["value"][2]=filterParentID.toStdString();
				//Add priority to filter
				//rule["filters"][filterCount][filterEntryCount]["value"][3]="1";
				
			}
		}

		tcCmd(buildFilterCmd(rule["filters"][filterCount])+" flowid "+flowID);
	}
	//tmp
	//qDebug() << getRuleStats(rule,ruleCount);

	return ruleCount;
}
//Build filter commands
QString HtbEngine::buildFilterCmd(Value rule)
{
	Path *path=NULL;
	Value filterValue=Value(objectValue);
	QString filter=QString();
	for(int matchCount=1; matchCount < rule.size(); matchCount++)
	{
		//if(rule[matchCount].empty())
		//	continue;
		path=new Path(rule[matchCount]["path"].asString());
		filterValue=path->resolve(constantValues->filters);
		int text=1,value=1,enumv=1,counter=1;
		while(text!=0 || value!=0)
		{
			if(!filterValue["text"][counter].isNull())
				if(QString().fromStdString(filterValue["text"][counter].asString())!="")
					filter=filter+" "+QString().fromStdString(filterValue["text"][counter].asString());
			if(!filterValue["value"][counter].isNull())
				filter=filter+" "+QString().fromStdString(rule[matchCount]["value"][counter].asString());
		//	if(!filterValue["enumv"][counter].isNull())
		//		filter=filter+" "+QString().fromStdString(filterValue["enumv"][counter][rule[matchCount]["enumv"][counter].asString()]["text"].asString());
			if(counter>=filterValue["text"].size()-1)
				text=0;
			if(counter>=filterValue["value"].size()-1)
				value=0;
		//	if(counter>=filterValue["enumv"].size()-1)
		//		enumv=0;
			counter++;
		}
	}
	if(filter[0].isSpace())
	{
		filter.remove(0,1);
	}
	return filter;
}
//Clean all tc stuff..
void HtbEngine::clear()
{	
	ruleCount=1;
	QString dev=QString().fromStdString(settings->get("device","").asString());
	QString ifb=QString().fromStdString(settings->get("ifb","").asString());
	tcCmd("qdisc del dev "+dev+" root");
	tcCmd("qdisc del dev "+ifb+" root");
	tcCmd("qdisc del dev "+dev+" ingress");
	tcCmd("qdisc del dev "+ifb+" ingress");
}
int HtbEngine::delRule(Value rule)
{
	return 0;
}
QString HtbEngine::getRuleStats(Value rule,int classID)
{
	QString dev=QString().fromStdString(settings->get("device","").asString());
	QString ifb=QString().fromStdString(settings->get("ifb","").asString());
	QString tc=QString().fromStdString(settings->get("tc_cmd","").asString());
	QStringList list;
	if(rule["direction"]=="incoming")
		list << QString("-s class show dev "+ifb+" parent 1:").split(" ");	
	else
		list << QString("-s class show dev "+dev+" parent 1:").split(" ");	
	//execTcStats->setProcessChannelMode(QProcess::MergedChannels);

	execTcStats->start(tc,list);
	//qDebug() << "\n" << list;
	if(!execTcStats->waitForFinished())
		qDebug() << "error:" << execTcStats->errorString();		
	else
	{
		//Search rate
		QByteArray tcOutput=execTcStats->readAllStandardOutput();
		int rateFound;
		QStringList tcOutputList=QString(tcOutput).split(' ');
		for(int index=0; index<tcOutputList.size(); index++)
		{
			if(tcOutputList[index]=="1:"+QString().setNum(classID))
			{
				rateFound=0;
				for(int subIndex=index; subIndex<tcOutputList.size(); subIndex++)
				{
					if(tcOutputList[subIndex]=="rate")
						rateFound++;
					if(rateFound==2)
					{
	//					qDebug() <<tcOutputList[subIndex+1];
						return QString(tcOutputList[subIndex+1]);
						break;
					}
				}
			break;	
			}
		}

	}
	return "";
}

void HtbEngine::debug()
{
	qDebug() << "*****************DEBUG*******************";
	QString dev=QString().fromStdString(settings->get("device","").asString());
	QString ifb=QString().fromStdString(settings->get("ifb","").asString());
	tcCmd("-s -d class ls dev "+dev);
	tcCmd("-s -d filter ls dev "+dev);
	tcCmd("-s -d qdisc ls dev "+dev);
	tcCmd("-s filter show parent ffff: dev "+dev);
	tcCmd("-s -d class ls dev "+ifb);
	tcCmd("-s -d filter ls dev "+ifb);
	tcCmd("-s -d qdisc ls dev "+ifb);
	tcCmd("-s filter show parent ffff: dev "+ifb);
	qDebug() << "*****************DEBUG END***************";
}

//
int HtbEngine::tcCmd(QString params)
{
	QString tc=QString().fromStdString(settings->get("tc_cmd","").asString());
	qDebug() << "\n" << params;
	int err=Model::addStrToFile(tc+" "+params,Model::getSettingsDir()+"tcrules.sh");
}
